﻿Imports System.ComponentModel
Imports System.Collections.ObjectModel
Imports System.Runtime.Serialization.Formatters.Binary
Imports System.IO
Imports System.Text.RegularExpressions

Public Class HeapViewModel
    Inherits ViewModelBase

    Public Sub New()
        Snapshots = New ObservableCollection(Of HeapSnapshot)
        Results = New ObservableCollection(Of ResultsItem)
        mAllResults = New List(Of ResultsItem)
    End Sub

    Public Sub AddSnapshot(ByVal snapshot As HeapSnapshot)
        Snapshots.Add(snapshot)
        RebuildResults()
    End Sub

    Public Sub RemoveSnapshot(ByVal snapshot As HeapSnapshot)
        Snapshots.Remove(snapshot)
        RebuildResults()
    End Sub

    Public Sub CloseSet()
        Snapshots.Clear()
        RebuildResults()
        CurrentSetFileName = Nothing
    End Sub

    Private mCurrentSetName As String
    Public Property CurrentSetFileName As String
        Get
            Return mCurrentSetName
        End Get
        Set(value As String)
            mCurrentSetName = value
            RaisePropertyChanged("CurrentSetFileName")
        End Set
    End Property

    Public Sub MoveSnapshotUp(ByVal snapshot As HeapSnapshot)
        Dim index As Integer = Snapshots.IndexOf(snapshot)
        If index = 0 Then Exit Sub
        Snapshots.Move(index, index - 1)
        RebuildResults()
    End Sub

    Public Sub MoveSnapshotDown(ByVal snapshot As HeapSnapshot)
        Dim index As Integer = Snapshots.IndexOf(snapshot)
        If index = Snapshots.Count - 1 Then Exit Sub
        Snapshots.Move(index, index + 1)
        RebuildResults()
    End Sub

    Private Sub RebuildResults()
        mAllResults.Clear()
        Dim snapshotIndex As Integer = 0
        For Each snap As HeapSnapshot In Snapshots
            For Each item As HeapSnapshotItem In snap.Items
                Dim sourceItem As HeapSnapshotItem = item
                Dim found = (From i In mAllResults Where i.ClassName = sourceItem.TypeName Select i).FirstOrDefault()
                If found Is Nothing Then
                    'add new item to collect info
                    Dim newItem As New ResultsItem() With {.ClassName = item.TypeName,
                                                           .MT = item.MT}
                    'add 0 counts for all snapshots
                    For j = 0 To Snapshots.Count - 1
                        newItem.Counts.Add(0)
                        newItem.Sizes.Add(0)
                    Next
                    'we always have exactly as many counts as we do snapshots, only set the one that applies to 
                    '   this iteration
                    mAllResults.Add(newItem)
                    found = newItem
                End If
                found.Counts.Item(snapshotIndex) = item.Count
                found.Sizes.Item(snapshotIndex) = item.Size
            Next
            snapshotIndex += 1
        Next
        Results = New ObservableCollection(Of ResultsItem)(mAllResults)
    End Sub

    Public Sub Save(ByVal savePath As String)
        'save current snapshots and results
        Dim formatter As New BinaryFormatter
        'serialize the object into a stream
        Dim ms As New MemoryStream
        formatter.Serialize(ms, New SavedResultsInfo() With {.Snapshots = Snapshots.ToList})
        Dim buffer() As Byte
        buffer = ms.ToArray
        Dim persist As String = System.Convert.ToBase64String(buffer)
        ms.Close()
        ms.Dispose()

        Dim fs As FileStream = File.Open(savePath, FileMode.Create, FileAccess.ReadWrite)
        Dim writer As New StreamWriter(fs)
        writer.Write(persist)
        writer.Flush()
        writer.Close()
        fs.Close()
        fs.Dispose()

        CurrentSetFileName = savePath

    End Sub

    Public Sub Open(ByVal openPath As String)
        Dim fs As FileStream = File.Open(openPath, FileMode.Open, FileAccess.Read)
        Dim reader As New StreamReader(fs)
        Dim persisted As String = reader.ReadToEnd()
        reader.Close()
        reader.Dispose()

        Dim ms As MemoryStream
        Dim buffer() As Byte = System.Convert.FromBase64String(persisted)
        ms = New MemoryStream(buffer)
        ms.Position = 0
        ms.Seek(0, SeekOrigin.Begin)
        Dim formatter As New BinaryFormatter
        Dim info As SavedResultsInfo = DirectCast(formatter.Deserialize(ms), SavedResultsInfo)
        ms.Close()
        ms.Dispose()
        Me.Snapshots = New ObservableCollection(Of HeapSnapshot)(info.Snapshots)
        CurrentSetFileName = openPath
        RebuildResults()
    End Sub

    Public Sub ExportToCSV(savePath As String)
        Dim bld As New System.Text.StringBuilder
        bld.AppendLine("TypeName,Count,Count%,Size,Size%,MT")
        For Each i As ResultsItem In Results
            bld.AppendFormat("""{0}"",{1},{2},{3},{4},{5},{6}", i.ClassName, i.CountString, i.CountChangePercent, i.SizeString, i.SizeChangePercent, i.MT, Environment.NewLine())
        Next

        Dim fs As FileStream = File.Open(savePath, FileMode.Create, FileAccess.ReadWrite)
        Dim writer As New StreamWriter(fs)
        writer.Write(bld.ToString)
        writer.Flush()
        writer.Close()
        fs.Close()
        fs.Dispose()

        Process.Start(savePath)
    End Sub

    Private mSnapshots As ObservableCollection(Of HeapSnapshot)
    Public Property Snapshots As ObservableCollection(Of HeapSnapshot)
        Get
            Return mSnapshots
        End Get
        Set(ByVal Value As ObservableCollection(Of HeapSnapshot))
            mSnapshots = Value
            RaisePropertyChanged("Snapshots")
        End Set
    End Property

    Private mAllResults As List(Of ResultsItem)
    Private mResults As ObservableCollection(Of ResultsItem)
    Public Property Results As ObservableCollection(Of ResultsItem)
        Get
            Return mResults
        End Get
        Set(ByVal Value As ObservableCollection(Of ResultsItem))
            mResults = Value
            RaisePropertyChanged("Results")
        End Set
    End Property

    Public Sub FilterTypeName(ByVal filter As String)
        If String.IsNullOrEmpty(filter) Then
            Results = New ObservableCollection(Of ResultsItem)(mAllResults)
        Else
            Dim filterRegex As New Regex(filter, RegexOptions.IgnoreCase)
            Dim filtered = (From i In mAllResults Where filterRegex.IsMatch(i.ClassName) Select i).ToList()
            Results = New ObservableCollection(Of ResultsItem)(filtered)
        End If
    End Sub

End Class
